package com.ruoyi.elastic.template;


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.ruoyi.elastic.domain.SkuInfo;
import org.apache.commons.lang3.StringUtils;

import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.StringTerms;
import org.elasticsearch.search.aggregations.metrics.cardinality.Cardinality;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.FieldSortBuilder;

import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.SearchQuery;
import org.springframework.stereotype.Service;


import java.util.*;

@Service
public class SkuInfoTemplate {

    private Logger logger = LoggerFactory.getLogger(SkuInfoTemplate.class);


    @Autowired
    private ElasticsearchTemplate elasticsearchTemplate;



    //实体类写法
    public void simpleSearchQuery(){
        SearchQuery searchQuery = new NativeSearchQueryBuilder()
                //.withIndices("skuinfo")
                //.withTypes("docs")
                .withQuery(QueryBuilders.matchAllQuery())
                .withSort(new FieldSortBuilder("price").order(SortOrder.ASC))
                .withPageable(PageRequest.of(0,5))
                .build();
        AggregatedPage<SkuInfo> skuInfos = elasticsearchTemplate.queryForPage(searchQuery,SkuInfo.class);
        logger.info("skuInfos:"+skuInfos);
        skuInfos.forEach(skuInfo -> logger.info(skuInfo.toString()));
        //AggregatedPage<Book> books = elasticsearchTemplate.queryForPage(searchQuery, Book.class);
        //AggregatedPage<String> stringAggregatedPage = elasticsearchTemplate.queryForPage(searchQuery,String.class);
        //stringAggregatedPage.forEach(s -> logger.info(s));
    }


    //无实体类直接写法
    public void simpleSearchQueryWithOutEntity(){
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder()
                .withIndices("skuinfo")
                .withTypes("docs")
                .withQuery(QueryBuilders.matchAllQuery())
                .addAggregation(AggregationBuilders.terms("brandName").field("brandName"))
                .withSort(new FieldSortBuilder("price").order(SortOrder.ASC))
                .withPageable(PageRequest.of(0,5));
        SearchQuery searchQuery = nativeSearchQueryBuilder.build();
        AggregatedPage<JSONObject> stringAggregatedPage = elasticsearchTemplate.queryForPage(searchQuery, JSONObject.class);
        logger.info(stringAggregatedPage.toString());
        logger.info(stringAggregatedPage.getAggregation("brandName").toString());
        StringTerms stringTerms = (StringTerms) stringAggregatedPage.getAggregation("brandName");
        List<String> result = new ArrayList<>();
        stringTerms.getBuckets().forEach(s -> {
            result.add("key:"+s.getKeyAsString()+"   value:"+s.getDocCount());
        });
        logger.info(result.toString());
        stringAggregatedPage.forEach(s -> logger.info(JSONObject.toJSONString(s)));
    }

    /**
     * 分组 写法
     *
     * @return
     */
    public Map simpleAggCardinality(){
        SearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withIndices("skuinfo")
                .withTypes("docs")
                .addAggregation(AggregationBuilders.cardinality("brandName").field("brandName"))
                .withPageable(PageRequest.of(0,5))
                .build();
        Map<String,Object> map = new HashMap<>();

        AggregatedPage<JSONObject> jsonObjects = elasticsearchTemplate.queryForPage(searchQuery,JSONObject.class);

        jsonObjects.forEach(s -> logger.info(JSONObject.toJSONString(jsonObjects)));

        Cardinality cardinality = jsonObjects.getAggregations().get("brandName");

        logger.info(cardinality.getValueAsString());

        map.put("cardinality:",cardinality.getValue());

        return map;

    }

    public Map complexSearchQueryWithOutEntity(Map<String,String> searchMap){
        //创建 查询的条件
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
        //无实体类要指定索引，文档
        nativeSearchQueryBuilder.withIndices("skuinfo");
        nativeSearchQueryBuilder.withTypes("docs");
        //分组
        nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms("brandName").field("brandName").size(30));
        nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms("categoryName").field("categoryName").size(30));
        nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms("skuSpecgroup").field("spec.keyword").size(30));
        //设置高亮的字段 设置前缀 和 后缀
        nativeSearchQueryBuilder.withHighlightFields(new HighlightBuilder.Field("name"));
        nativeSearchQueryBuilder.withHighlightBuilder(new HighlightBuilder().preTags("<em style=\\\"color:red\\\">\").postTags(\"</em>"));

//        if (searchMap != null && searchMap.size() > 0){
//            String keywords = searchMap.get("keywords");
//            nativeSearchQueryBuilder.withQuery(QueryBuilders.multiMatchQuery(keywords,"brandName","categoryName","skuSpecgroup"));
//        }

        //bool查询
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        String category = searchMap.get("categoryName");
        String startTime = searchMap.get("startTime");
        String endTime  = searchMap.get("endTime");
        String brandName = searchMap.get("brandName");

        if (StringUtils.isNotBlank(category)){
            boolQueryBuilder.must(QueryBuilders.termQuery("categoryName",category));
        }

        if (StringUtils.isNotBlank(startTime) && StringUtils.isNotBlank(endTime)){
            boolQueryBuilder.must(QueryBuilders.rangeQuery("createTime").gte(startTime).lte(endTime));
        }

        if (StringUtils.isNotBlank(brandName)){
            boolQueryBuilder.must(QueryBuilders.termQuery("brandName",brandName));
        }

        //should 在无must的情况下必须满足一个条件
        if (StringUtils.isNotBlank(brandName)){
            boolQueryBuilder.should(QueryBuilders.termsQuery("brandName",brandName,"华为"));
            boolQueryBuilder.should(QueryBuilders.termsQuery("category",category));
        }

        //排序操作
        String sortField = searchMap.get("sortField");
        String sortRule = searchMap.get("sortRule");
        if (StringUtils.isNotBlank(sortField) && StringUtils.isNotBlank(sortRule)){
            nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort(sortField).order(sortRule.equalsIgnoreCase("ASC")?SortOrder.ASC:SortOrder.DESC));
        }

        //分页操纵
        String pageNum = searchMap.get("pageNum");
        String pageSize = searchMap.get("pageSize");
        if (StringUtils.isNotBlank(pageNum)){
            nativeSearchQueryBuilder.withPageable(PageRequest.of(Integer.parseInt(pageNum),StringUtils.isNotBlank(pageSize)?Integer.parseInt(pageSize):100));
        }

        // 构建查询对象(封装了查询的语法)
        NativeSearchQuery nativeSearchQuery = nativeSearchQueryBuilder.build();
        // 执行查询
        AggregatedPage<JSONObject> jsonObjects = elasticsearchTemplate.queryForPage(nativeSearchQuery,JSONObject.class);
        logger.info(jsonObjects.toString());
        jsonObjects.forEach(s -> logger.info(JSONObject.toJSONString(s)));
        StringTerms  stringTermsBrandName = (StringTerms) jsonObjects.getAggregation("brandName");
        List<String> brandList = getAggregation(stringTermsBrandName);
        StringTerms stringTermsCategory = (StringTerms) jsonObjects.getAggregation("category");
        List<String> categoryList = getAggregation(stringTermsCategory);
        StringTerms stringTermsSpec = (StringTerms) jsonObjects.getAggregation("skuSpecgroup");
        List<String> specList = getAggregationComplex(stringTermsSpec);

        //获取结果
        List<JSONObject> content = jsonObjects.getContent(); //当前页中数据集合
        int totalPages = jsonObjects.getTotalPages();//总页数
        long totalElements = jsonObjects.getTotalElements(); //获取总记录数

        Map<String,Object> result = new HashMap<>();
        result.put("brandName",brandList);
        result.put("category",categoryList);
        result.put("specList",specList);
        result.put("row",content);
        result.put("totalPages",totalPages);
        result.put("totalElements",totalElements);
        logger.info(result.toString());
        return  result;
    }

    // 获取聚合分组结果
    List<String> getAggregation(StringTerms stringTerms){
        List<String> List = new ArrayList<>();
        if (stringTerms != null){
            //!.获取分组结果
            for (StringTerms.Bucket bucket: stringTerms.getBuckets()){
                String keyAsString = bucket.getKeyAsString();
                List.add("key:"+keyAsString+"   values:"+bucket.getDocCount());
            }
        }
        return List;
    }

    //complex
    List<String> getAggregationComplex(StringTerms stringTerms){
        Map<String, Set<String>> Map = new HashMap<>();
        Set<String> values = new HashSet<String>();
        if (values != null){
            for (StringTerms.Bucket bucket: stringTerms.getBuckets()){
                String keyAsString = bucket.getKeyAsString();
                Map<String, String> map = JSON.parseObject(keyAsString, Map.class);
                for (Map.Entry<String,String> stringStringEntry:map.entrySet()){
                    String key = stringStringEntry.getKey();
                    String value = stringStringEntry.getValue();
                    values = Map.get(key);
                    if (values == null) {
                        values = new HashSet<>();
                    }
                    values.add(value);
                    //4.提取map中的值放入到返回的map中
                    Map.put(key, values);
                }
            }
        }
        return null;
    }

}
